1   /*
2    * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-
26  import xmlkit.XMLKit.*;
27  
28  import java.util.*;
29  import java.security.MessageDigest;
30  import java.nio.ByteBuffer;
31  import xmlkit.XMLKit.Element;
32  /*
33   * @author jrose
34   */
35  public abstract class ClassSyntax {
36  
37      public interface GetCPIndex {
38  
39          int getCPIndex(int tag, String name);  // cp finder
40      }
41      public static final int CONSTANT_Utf8 = 1,
42              CONSTANT_Integer = 3,
43              CONSTANT_Float = 4,
44              CONSTANT_Long = 5,
45              CONSTANT_Double = 6,
46              CONSTANT_Class = 7,
47              CONSTANT_String = 8,
48              CONSTANT_Fieldref = 9,
49              CONSTANT_Methodref = 10,
50              CONSTANT_InterfaceMethodref = 11,
51              CONSTANT_NameAndType = 12;
52      private static final String[] cpTagName = {
53          /* 0:  */null,
54          /* 1:  */ "Utf8",
55          /* 2:  */ null,
56          /* 3:  */ "Integer",
57          /* 4:  */ "Float",
58          /* 5:  */ "Long",
59          /* 6:  */ "Double",
60          /* 7:  */ "Class",
61          /* 8:  */ "String",
62          /* 9:  */ "Fieldref",
63          /* 10: */ "Methodref",
64          /* 11: */ "InterfaceMethodref",
65          /* 12: */ "NameAndType",
66          null
67      };
68      private static final Set<String> cpTagNames;
69  
70      static {
71          Set<String> set = new HashSet<String>(Arrays.asList(cpTagName));
72          set.remove(null);
73          cpTagNames = Collections.unmodifiableSet(set);
74      }
75      public static final int ITEM_Top = 0, // replicates by [1..4,1..4]
76              ITEM_Integer = 1, // (ditto)
77              ITEM_Float = 2,
78              ITEM_Double = 3,
79              ITEM_Long = 4,
80              ITEM_Null = 5,
81              ITEM_UninitializedThis = 6,
82              ITEM_Object = 7,
83              ITEM_Uninitialized = 8,
84              ITEM_ReturnAddress = 9,
85              ITEM_LIMIT = 10;
86      private static final String[] itemTagName = {
87          "Top",
88          "Integer",
89          "Float",
90          "Double",
91          "Long",
92          "Null",
93          "UninitializedThis",
94          "Object",
95          "Uninitialized",
96          "ReturnAddress",};
97      private static final Set<String> itemTagNames;
98  
99      static {
100         Set<String> set = new HashSet<String>(Arrays.asList(itemTagName));
101         set.remove(null);
102         itemTagNames = Collections.unmodifiableSet(set);
103     }
104     protected static final HashMap<String, String> attrTypesBacking;
105     protected static final Map<String, String> attrTypesInit;
106 
107     static {
108         HashMap<String, String> at = new HashMap<String, String>();
109 
110         //at.put("*.Deprecated", "<deprecated=true>");
111         //at.put("*.Synthetic", "<synthetic=true>");
112         ////at.put("Field.ConstantValue", "<constantValue=>KQH");
113         //at.put("Class.SourceFile", "<sourceFile=>RUH");
114         at.put("Method.Bridge", "<Bridge>");
115         at.put("Method.Varargs", "<Varargs>");
116         at.put("Class.Enum", "<Enum>");
117         at.put("*.Signature", "<Signature>RSH");
118         //at.put("*.Deprecated", "<Deprecated>");
119         //at.put("*.Synthetic", "<Synthetic>");
120         at.put("Field.ConstantValue", "<ConstantValue>KQH");
121         at.put("Class.SourceFile", "<SourceFile>RUH");
122         at.put("Class.InnerClasses", "NH[<InnerClass><class=>RCH<outer=>RCH<name=>RUH<flags=>FH]");
123         at.put("Code.LineNumberTable", "NH[<LineNumber><bci=>PH<line=>H]");
124         at.put("Code.LocalVariableTable", "NH[<LocalVariable><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
125         at.put("Code.LocalVariableTypeTable", "NH[<LocalVariableType><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
126         at.put("Method.Exceptions", "NH[<Exception><name=>RCH]");
127         at.put("Method.Code", "<Code>...");
128         at.put("Code.StackMapTable", "<Frame>...");
129         //at.put("Code.StkMapX", "<FrameX>...");
130         if (true) {
131             at.put("Code.StackMapTable",
132                     "[NH[<Frame>(1)]]"
133                     + "[TB"
134                     + "(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79"
135                     + ",80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95"
136                     + ",96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111"
137                     + ",112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127"
138                     + ")[<SameLocals1StackItemFrame>(4)]"
139                     + "(247)[<SameLocals1StackItemExtended>H(4)]"
140                     + "(248)[<Chop3>H]"
141                     + "(249)[<Chop2>H]"
142                     + "(250)[<Chop1>H]"
143                     + "(251)[<SameFrameExtended>H]"
144                     + "(252)[<Append1>H(4)]"
145                     + "(253)[<Append2>H(4)(4)]"
146                     + "(254)[<Append3>H(4)(4)(4)]"
147                     + "(255)[<FullFrame>H(2)(3)]"
148                     + "()[<SameFrame>]]"
149                     + "[NH[<Local>(4)]]"
150                     + "[NH[<Stack>(4)]]"
151                     + "[TB"
152                     + ("(0)[<Top>]"
153                     + "(1)[<ItemInteger>](2)[<ItemFloat>](3)[<ItemDouble>](4)[<ItemLong>]"
154                     + "(5)[<ItemNull>](6)[<ItemUninitializedThis>]"
155                     + "(7)[<ItemObject><class=>RCH]"
156                     + "(8)[<ItemUninitialized><bci=>PH]"
157                     + "()[<ItemUnknown>]]"));
158         }
159 
160         at.put("Class.EnclosingMethod", "<EnclosingMethod><class=>RCH<desc=>RDH");//RDNH
161 
162         // Layouts of metadata attrs:
163         String vpf = "[<RuntimeVisibleAnnotation>";
164         String ipf = "[<RuntimeInvisibleAnnotation>";
165         String apf = "[<Annotation>";
166         String mdanno2 = ""
167                 + "<type=>RSHNH[<Member><name=>RUH(3)]]"
168                 + ("[TB"
169                 + "(\\B,\\C,\\I,\\S,\\Z)[<value=>KIH]"
170                 + "(\\D)[<value=>KDH]"
171                 + "(\\F)[<value=>KFH]"
172                 + "(\\J)[<value=>KJH]"
173                 + "(\\c)[<class=>RSH]"
174                 + "(\\e)[<type=>RSH<name=>RUH]"
175                 + "(\\s)[<String>RUH]"
176                 + "(\\@)[(2)]"
177                 + "(\\[)[NH[<Element>(3)]]"
178                 + "()[]"
179                 + "]");
180         String visanno = "[NH[(2)]][(1)]" + vpf + mdanno2;
181         String invanno = "[NH[(2)]][(1)]" + ipf + mdanno2;
182         String vparamanno = ""
183                 + "[NB[<RuntimeVisibleParameterAnnotation>(1)]][NH[(2)]]"
184                 + apf + mdanno2;
185         String iparamanno = ""
186                 + "[NB[<RuntimeInvisibleParameterAnnotation>(1)]][NH[(2)]]"
187                 + apf + mdanno2;
188         String mdannodef = "[<AnnotationDefault>(3)][(1)]" + apf + mdanno2;
189         String[] mdplaces = {"Class", "Field", "Method"};
190         for (String place : mdplaces) {
191             at.put(place + ".RuntimeVisibleAnnotations", visanno);
192             at.put(place + ".RuntimeInvisibleAnnotations", invanno);
193         }
194         at.put("Method.RuntimeVisibleParameterAnnotations", vparamanno);
195         at.put("Method.RuntimeInvisibleParameterAnnotations", iparamanno);
196         at.put("Method.AnnotationDefault", mdannodef);
197 
198         attrTypesBacking = at;
199         attrTypesInit = Collections.unmodifiableMap(at);
200     }
201 
202     ;
203     private static final String[] jcovAttrTypes = {
204         "Code.CoverageTable=NH[<Coverage><bci=>PH<type=>H<line=>I<pos=>I]",
205         "Code.CharacterRangeTable=NH[<CharacterRange><bci=>PH<endbci=>POH<from=>I<to=>I<flag=>H]",
206         "Class.SourceID=<SourceID><id=>RUH",
207         "Class.CompilationID=<CompilationID><id=>RUH"
208     };
209     protected static final String[][] modifierNames = {
210         {"public"},
211         {"private"},
212         {"protected"},
213         {"static"},
214         {"final"},
215         {"synchronized"},
216         {null, "volatile", "bridge"},
217         {null, "transient", "varargs"},
218         {null, null, "native"},
219         {"interface"},
220         {"abstract"},
221         {"strictfp"},
222         {"synthetic"},
223         {"annotation"},
224         {"enum"},};
225     protected static final String EIGHT_BIT_CHAR_ENCODING = "ISO8859_1";
226     protected static final String UTF8_ENCODING = "UTF8";
227     // What XML tags are used by this syntax, apart from attributes?
228     protected static final Set<String> nonAttrTags;
229 
230     static {
231         HashSet<String> tagSet = new HashSet<String>();
232         Collections.addAll(tagSet, new String[]{
233                     "ConstantPool",// the CP
234                     "Class", // the class
235                     "Interface", // implemented interfaces
236                     "Method", // methods
237                     "Field", // fields
238                     "Handler", // exception handler pseudo-attribute
239                     "Attribute", // unparsed attribute
240                     "Bytes", // bytecodes
241                     "Instructions" // bytecodes, parsed
242                 });
243         nonAttrTags = Collections.unmodifiableSet(tagSet);
244     }
245 
246     // Accessors.
247     public static Set<String> nonAttrTags() {
248         return nonAttrTags;
249     }
250 
251     public static String cpTagName(int t) {
252         t &= 0xFF;
253         String ts = null;
254         if (t < cpTagName.length) {
255             ts = cpTagName[t];
256         }
257         if (ts != null) {
258             return ts;
259         }
260         return ("UnknownTag" + (int) t).intern();
261     }
262 
263     public static int cpTagValue(String name) {
264         for (int t = 0; t < cpTagName.length; t++) {
265             if (name.equals(cpTagName[t])) {
266                 return t;
267             }
268         }
269         return 0;
270     }
271 
272     public static String itemTagName(int t) {
273         t &= 0xFF;
274         String ts = null;
275         if (t < itemTagName.length) {
276             ts = itemTagName[t];
277         }
278         if (ts != null) {
279             return ts;
280         }
281         return ("UnknownItem" + (int) t).intern();
282     }
283 
284     public static int itemTagValue(String name) {
285         for (int t = 0; t < itemTagName.length; t++) {
286             if (name.equals(itemTagName[t])) {
287                 return t;
288             }
289         }
290         return -1;
291     }
292 
293     public void addJcovAttrTypes() {
294         addAttrTypes(jcovAttrTypes);
295     }
296     // Public methods for declaring attribute types.
297     protected Map<String, String> attrTypes = attrTypesInit;
298 
299     public void addAttrType(String opt) {
300         int eqpos = opt.indexOf('=');
301         addAttrType(opt.substring(0, eqpos), opt.substring(eqpos + 1));
302     }
303 
304     public void addAttrTypes(String[] opts) {
305         for (String opt : opts) {
306             addAttrType(opt);
307         }
308     }
309 
310     private void checkAttr(String attr) {
311         if (!attr.startsWith("Class.")
312                 && !attr.startsWith("Field.")
313                 && !attr.startsWith("Method.")
314                 && !attr.startsWith("Code.")
315                 && !attr.startsWith("*.")) {
316             throw new IllegalArgumentException("attr name must start with 'Class.', etc.");
317         }
318         String uattr = attr.substring(attr.indexOf('.') + 1);
319         if (nonAttrTags.contains(uattr)) {
320             throw new IllegalArgumentException("attr name must not be one of " + nonAttrTags);
321         }
322     }
323 
324     private void checkAttrs(Map<String, String> at) {
325         for (String attr : at.keySet()) {
326             checkAttr(attr);
327         }
328     }
329 
330     private void modAttrs() {
331         if (attrTypes == attrTypesInit) {
332             // Make modifiable.
333             attrTypes = new HashMap<String, String>(attrTypesBacking);
334         }
335     }
336 
337     public void addAttrType(String attr, String fmt) {
338         checkAttr(attr);
339         modAttrs();
340         attrTypes.put(attr, fmt);
341     }
342 
343     public void addAttrTypes(Map<String, String> at) {
344         checkAttrs(at);
345         modAttrs();
346         attrTypes.putAll(at);
347     }
348 
349     public Map<String, String> getAttrTypes() {
350         if (attrTypes == attrTypesInit) {
351             return attrTypes;
352         }
353         return Collections.unmodifiableMap(attrTypes);
354     }
355 
356     public void setAttrTypes(Map<String, String> at) {
357         checkAttrs(at);
358         modAttrs();
359         attrTypes.keySet().retainAll(at.keySet());
360         attrTypes.putAll(at);
361     }
362 
363     // attr format helpers
364     protected static boolean matchTag(int tagValue, String caseStr) {
365         //System.out.println("matchTag "+tagValue+" in "+caseStr);
366         for (int pos = 0, max = caseStr.length(), comma;
367                 pos < max;
368                 pos = comma + 1) {
369             int caseValue;
370             if (caseStr.charAt(pos) == '\\') {
371                 caseValue = caseStr.charAt(pos + 1);
372                 comma = pos + 2;
373                 assert (comma == max || caseStr.charAt(comma) == ',');
374             } else {
375                 comma = caseStr.indexOf(',', pos);
376                 if (comma < 0) {
377                     comma = max;
378                 }
379                 caseValue = Integer.parseInt(caseStr.substring(pos, comma));
380             }
381             if (tagValue == caseValue) {
382                 return true;
383             }
384         }
385         return false;
386     }
387 
388     protected static String[] getBodies(String type) {
389         ArrayList<String> bodies = new ArrayList<String>();
390         for (int i = 0; i < type.length();) {
391             String body = getBody(type, i);
392             bodies.add(body);
393             i += body.length() + 2;  // skip body and brackets
394         }
395         return bodies.toArray(new String[bodies.size()]);
396     }
397 
398     protected static String getBody(String type, int i) {
399         assert (type.charAt(i) == '[');
400         int next = ++i;  // skip bracket
401         for (int depth = 1; depth > 0; next++) {
402             switch (type.charAt(next)) {
403                 case '[':
404                     depth++;
405                     break;
406                 case ']':
407                     depth--;
408                     break;
409                 case '(':
410                     next = type.indexOf(')', next);
411                     break;
412                 case '<':
413                     next = type.indexOf('>', next);
414                     break;
415             }
416             assert (next > 0);
417         }
418         --next;  // get before bracket
419         assert (type.charAt(next) == ']');
420         return type.substring(i, next);
421     }
422 
423     public Element makeCPDigest(int length) {
424         MessageDigest md;
425         try {
426             md = MessageDigest.getInstance("MD5");
427         } catch (java.security.NoSuchAlgorithmException ee) {
428             throw new Error(ee);
429         }
430         int items = 0;
431         for (Element e : cpool.elements()) {
432             if (items == length) {
433                 break;
434             }
435             if (cpTagNames.contains(e.getName())) {
436                 items += 1;
437                 md.update((byte) cpTagValue(e.getName()));
438                 try {
439                     md.update(e.getText().toString().getBytes(UTF8_ENCODING));
440                 } catch (java.io.UnsupportedEncodingException ee) {
441                     throw new Error(ee);
442                 }
443             }
444         }
445         ByteBuffer bb = ByteBuffer.wrap(md.digest());
446         String l0 = Long.toHexString(bb.getLong(0));
447         String l1 = Long.toHexString(bb.getLong(8));
448         while (l0.length() < 16) {
449             l0 = "0" + l0;
450         }
451         while (l1.length() < 16) {
452             l1 = "0" + l1;
453         }
454         return new Element("Digest",
455                 "length", "" + items,
456                 "bytes", l0 + l1);
457     }
458 
459     public Element getCPDigest(int length) {
460         if (length == -1) {
461             length = cpool.countAll(XMLKit.elementFilter(cpTagNames));
462         }
463         for (Element md : cpool.findAllElements("Digest").elements()) {
464             if (md.getAttrLong("length") == length) {
465                 return md;
466             }
467         }
468         Element md = makeCPDigest(length);
469         cpool.add(md);
470         return md;
471     }
472 
473     public Element getCPDigest() {
474         return getCPDigest(-1);
475     }
476 
477     public boolean checkCPDigest(Element md) {
478         return md.equals(getCPDigest((int) md.getAttrLong("length")));
479     }
480 
481     public static int computeInterfaceNum(String intMethRef) {
482         intMethRef = intMethRef.substring(1 + intMethRef.lastIndexOf(' '));
483         if (!intMethRef.startsWith("(")) {
484             return -1;
485         }
486         int signum = 1;  // start with one for "this"
487         scanSig:
488         for (int i = 1; i < intMethRef.length(); i++) {
489             char ch = intMethRef.charAt(i);
490             signum++;
491             switch (ch) {
492                 case ')':
493                     --signum;
494                     break scanSig;
495                 case 'L':
496                     i = intMethRef.indexOf(';', i);
497                     break;
498                 case '[':
499                     while (ch == '[') {
500                         ch = intMethRef.charAt(++i);
501                     }
502                     if (ch == 'L') {
503                         i = intMethRef.indexOf(';', i);
504                     }
505                     break;
506             }
507         }
508         int num = (signum << 8) | 0;
509         //System.out.println("computeInterfaceNum "+intMethRef+" => "+num);
510         return num;
511     }
512     // Protected state for representing the class file.
513     protected Element cfile;          // <ClassFile ...>
514     protected Element cpool;          // <ConstantPool ...>
515     protected Element klass;          // <Class ...>
516     protected Element currentMember;  // varies during scans
517     protected Element currentCode;    // varies during scans
518 }